package cn.tedu.lucky.draw.server.service.impl;

import cn.tedu.lucky.draw.server.common.enums.DrawEnum;
import cn.tedu.lucky.draw.server.common.handler.DrawException;
import cn.tedu.lucky.draw.server.protocol.po.*;
import cn.tedu.lucky.draw.server.protocol.query.ActivityQuery;
import cn.tedu.lucky.draw.server.repository.ActivityRepository;
import cn.tedu.lucky.draw.server.repository.ActivityRuleRepository;
import cn.tedu.lucky.draw.server.repository.ResultRepository;
import cn.tedu.lucky.draw.server.repository.RuleRepository;
import cn.tedu.lucky.draw.server.service.IActivityService;
import cn.tedu.lucky.draw.server.service.IPrizeService;
import netscape.javascript.JSObject;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements IActivityService {

    List<Activity> activities;

    @Autowired
    private ActivityRepository activityRepository;
    @Autowired
    private ActivityRuleRepository activityRuleRepository;

    @Autowired
    private RuleRepository ruleRepository;

    @Autowired
    private ResultRepository resultRepository;

    @Autowired
    private IPrizeService prizeService;



    @Override
    public List<Activity> getAll(ActivityQuery activityQuery) {
        if (activities==null){
            activities = activityRepository.findAll();
        }

        String name =  activityQuery.getName();
        Integer ststus=activityQuery.getStatus();
        return activities.stream()
                .filter(activity -> name==null || name == "" || activity.getName().equals(name))
                .filter(activity -> ststus==null || ststus.equals(activity.getStatus()))
                .collect(Collectors.toList());
    }

    @Override
    public void save(Activity activity) {
        activity.setStatus(0);
        LocalDateTime now = LocalDateTime.now();
        activity.setCreateTime(now);
        activity.setUpdateTime(now);
        Activity saveActivity = activityRepository.save(activity);
        if (saveActivity==null){
            throw new DrawException(DrawEnum.INSERT_ERROR);
        }
        activities = null;
    }

    @Override
    public Activity findById(Long id) {
        Optional<Activity> optional = activityRepository.findById(id);
        Activity activity = optional.orElse(new Activity());
        return activity;
    }

    @Override
    public List<Activity> getAllActivities() {
        Integer status = Integer.valueOf(DrawEnum.START.getCode());
        List<Activity> activityList = activityRepository.findByStatus(status);
        return activityList;
    }

    @Override
    public Prize trigger(Long activityId)  throws JSONException {
        //1.先获取活动规则，判断是否符合抽奖条件
        List<ActivityRule> activityRuleList = activityRuleRepository.findByActivityId(activityId);
        //获取规则ID
        List<Long> ruleIds = activityRuleList.stream().map(ActivityRule::getRuleId).collect(Collectors.toList());
        //根据规则ID获取规则列表,比如抽奖次数规则、中奖概率规则等
        List<Rule> ruleList = ruleRepository.findAllById(ruleIds);
        //是否降低中奖概率，默认为false表示不降低
        boolean value=false;

        //1.1 检测抽奖次数
        for (Rule rule : ruleList) {
            if (rule.getType()==1) {
                //获取当前活动配置的抽奖次数
                JSONObject jsonObject = new JSONObject(rule.getContent());
                int count = jsonObject.getInt("count");
                //获取当前用户已经参与的次数，针对当前活动而言
                int result = resultRepository.countByActivityIdAndUserId(activityId, getUserId());
                if (result >= count){
                    throw new DrawException(DrawEnum.TIMES_EXHAUSTED);
                }
            }else{
                //判断中奖概率是否降低
                JSONObject jsonObject = new JSONObject(rule.getContent());
                value = jsonObject.getBoolean("value");
            }
        }
        //2.获取奖品列表，计算奖品总逆价值
        List<Prize> prizeList = prizeService.findAllByActivityId(activityId);
        double totalValue = prizeList.stream().mapToDouble(prize -> 1.0 / prize.getValue()).sum();
        Map<Prize, Double> prizeDoubleHashMap = new HashMap<>();
        //3.根据奖品价格分配中奖概率，随机抽奖
        // 如果需要降低概率，则素有概率值乘以0.5，降低中奖概率
        double decreaseFactor = value? 0.5 : 1.0;
        for (Prize prize : prizeList) {
            /**
             * 假设有三个奖品：A、B、C ，他们的价格分别为100、200、300，则他们的中奖概率为：
             * A：1/100 = 0.01
             * B：1/200 = 0.005
             * C：1/300 = 0.0033
             * 总逆价值：A+B+C =0.01+0.005+0.0033 =0.0183
             * A的中奖概率：0.01/0.0183 = 0.546
             * B的中奖概率：0.005/0.0183 = 0.273
             * C的中奖概率：0.0033/0.0183 = 0.180
             */
            double individual =(1.0 / prize.getValue())/totalValue;
            prizeDoubleHashMap.put(prize, individual*decreaseFactor);
        }
        //4.执行抽奖
        double randomValue=new Random().nextDouble();
        //初始概率，累计概率
        double cumulative=0.0;
        for (Map.Entry<Prize, Double> entry : prizeDoubleHashMap.entrySet()) {
            Prize prize = entry.getKey();
            Double probabliity = entry.getValue();
            cumulative += probabliity;
            if (randomValue <= cumulative) {
                //减少奖品库存
                prize.setStock(prize.getStock()-1);
                //更新奖品库存
                prizeService.save(prize);
                //保存抽奖结果
                saveActivityResult(activityId,prize.getId());
                //返回奖品信息
                return prize;
            }

        }
        //如果没有中奖，则返回
        Prize prize = new Prize();
        prize.setName("谢谢参与");
        prize.setId(999999999L);
        prize.setImg("https://img.tukuppt.com/png_preview/00/44/90/VDa2b4drT2.jpg%21/fw/780");
        //保存抽奖结果
        saveActivityResult(activityId,prize.getId());
        return prize;
    }

    public void saveActivityResult(Long activityId,Long prizeId){
        Result result = new Result();
        result.setActivityId(activityId);
        result.setPrizeId(prizeId);
        result.setUserId(getUserId());
        result.setWinTime(LocalDateTime.now());
        result.setStatus(0);//未发放
        resultRepository.save(result);
    }


    public Long getUserId() {
        return 99999L;
    }
}



